package com.zjw.zy.heart.service.impl;

import com.zjw.zy.heart.bean.BindLoveInfo;
import com.zjw.zy.heart.wbsocket.socket.WsMessage;
import com.zjw.zy.heart.configuration.TaskPool;
import com.zjw.zy.heart.constant.BindLoveState;
import com.zjw.zy.heart.constant.RegisterUserResult;
import com.zjw.zy.heart.domain.User;
import com.zjw.zy.heart.mapper.UserMapper;
import com.zjw.zy.heart.service.IUserService;
import com.zjw.zy.heart.userpool.IUserPool;
import com.zjw.zy.heart.utils.LogUtils;
import com.zjw.zy.heart.wbsocket.WsSender;
import com.zjw.zy.heart.wbsocket.IWSMessageHandler;
import com.zjw.zy.heart.wbsocket.WsDispatcher;
import jakarta.websocket.Session;
import lombok.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;


@Service
public class UserServiceImpl implements IUserService, IWSMessageHandler {

    private static final String TAG = "UserServiceImpl";

    private UserMapper userMapper;

    private IUserPool userPool;

    private static final String CACHE_USER_PRE_KEY = "user-cache-";

    private RedisTemplate<String, User> userRedisTemplate;

    private final Map<Long, CountDownLatch> awaitResponseHolder = new ConcurrentHashMap<>();
    private final Map<Long, BindLoveInfo> awaitResponseResultHolder = new ConcurrentHashMap<>();

    @Override
    public void onBindLove(WsMessage message) {
        LogUtils.d(TAG, "bindLove() called with: message = [" + message + "]");
        String someone = message.getTo();
        String me = message.getFrom();
        User someoneUser = queryUser(someone);
        Session meSession = userPool.querySession(me);

        BindLoveInfo info = message.getPayload(BindLoveInfo.class);
        if (someoneUser == null) {
            //还不存在，需要注册
            LogUtils.e(TAG, "onBindLove: not register");
            info.setState(BindLoveState.BIND_TARGET_NOT_REGISTER);
            WsSender.convertAndSendToClient(meSession, message);
            return;
        }

        if (someoneUser.getLoveusername() != null && !someoneUser.getLoveusername().isEmpty()) {
            //对方已经绑定
            LogUtils.e(TAG, "onBindLove:target has bind");
            info.setState(BindLoveState.BIND_TARGET_HAS_BIND);
            WsSender.convertAndSendToClient(meSession, message);
            return;
        }

        Session session = userPool.querySession(someoneUser.getUsername());
        if (session == null || !session.isOpen()) {
            //对方未在线
            LogUtils.e(TAG, "onBindLove: target not online");
            info.setState(BindLoveState.BIND_TARGET_OFFLINE);
            WsSender.convertAndSendToClient(meSession, message);
            return;
        }

        if (meSession == null || !meSession.isOpen()) {
            LogUtils.e(TAG, "onBindLove: meSession == null");
            return;
        }

        info.setState(BindLoveState.BIND_WAIT_RESPONSE);
        boolean success = WsSender.convertAndSendToClient(meSession, message);

        if (success) {
            onStartInviteLover(message);
        }
    }

    @Async(TaskPool.CONCURRENT_POOL)
    public void onStartInviteLover(WsMessage message) {
        LogUtils.d(TAG, "onStartInviteLover() called with: message = [" + message + "]");
        BindLoveInfo info = message.getPayload(BindLoveInfo.class);
        try {
            info.setState(BindLoveState.BIND_INVITE_LOVE);
            message.setPayload(info);
            WsSender.convertAndSendToClient(userPool.querySession(message.getTo()), message);
            CountDownLatch count = new CountDownLatch(1);
            awaitResponseHolder.put(info.getId(), count);
            //等待最长时间60s
            count.await(60, TimeUnit.SECONDS);
            info = awaitResponseResultHolder.remove(info.getId());
            if (info != null && info.getAccept()) {
                //进一步，验证验证码，发给邀请者，验证码简单为id的后十位
                onCheckCode(message, info);
            } else {
                onBindFailed(message, info);
            }

        } catch (Exception e) {
            LogUtils.e(TAG, "notifyLoveUser", e);
            onBindFailed(message, info);
        }

    }

    private void onCheckCode(WsMessage msg, BindLoveInfo info) {
        LogUtils.d(TAG, "notifyInviteUser() called with: msg = [" + msg + "], info = [" + info + "]");
        info.setState(BindLoveState.BIND_REQUEST_CODE);
        msg.setPayload(info);
        WsSender.convertAndSendToClient(userPool.querySession(msg.getFrom()), msg);
        CountDownLatch count = new CountDownLatch(1);
        awaitResponseHolder.put(info.getId(), count);
        try {
            //再次等待60s
            count.await(60, TimeUnit.SECONDS);
            info = awaitResponseResultHolder.remove(info.getId());
            if (info != null && info.getState() == BindLoveState.BIND_PASS_CODE) {
                onBindSuccess(msg, info);
            } else {
                onBindFailed(msg, info);
            }
        } catch (InterruptedException e) {
            LogUtils.e(TAG, "onCheckCode", e);
            onBindFailed(msg, info);
        }
    }

    private void onBindSuccess(WsMessage msg, BindLoveInfo info) {
        LogUtils.d(TAG, "onBindSuccess() called with: msg = [" + msg + "], info = [" + info + "]");
        info.setState(BindLoveState.BIND_SUCCESS);
        //变更数据库
        User one = userMapper.selectByPrimaryKey(msg.getFrom());
        User two = userMapper.selectByPrimaryKey(msg.getTo());
        one.setLoveusername(two.getUsername());
        two.setLoveusername(one.getUsername());
        //set more info
        one.putInfo(User.BIND_INFO_KEY, info);
        two.putInfo(User.BIND_INFO_KEY, info);
        userMapper.updateByPrimaryKey(one);
        userMapper.updateByPrimaryKey(two);
        msg.setPayload(info);
        msg.jsonPayload();
        WsSender.sendToClient(userPool.querySession(msg.getFrom()), msg);
        WsSender.sendToClient(userPool.querySession(msg.getTo()), msg);
    }

    private void onBindFailed(WsMessage message, BindLoveInfo info) {
        LogUtils.d(TAG, "onBindFailed() called with: me = [" + message + "]");

        if (!info.getAccept()) {
            info.setState(BindLoveState.BIND_REJECT);
        } else {
            //unknow
            info.setState(BindLoveState.BIND_FAIL);
        }
        message.setPayload(info);
        WsSender.convertAndSendToClient(userPool.querySession(message.getFrom()), message);
    }

    @Override
    public void onHandleWsMessage(@NonNull WsMessage message) {
        LogUtils.d(TAG, "onNotifyBindResult() called with: wechatMessage = [" + message + "]");
        //这里收到三种消息，来自绑定发起者，来自lover的答复，来自发起者的验证码自检
        BindLoveInfo payload = message.getPayload(BindLoveInfo.class);
        if (payload.getState().equals(BindLoveState.BIND_INVITE_LOVE)) {
            onBindLove(message);
            return;
        }

        awaitResponseResultHolder.put(payload.getId(), payload);
        CountDownLatch count = awaitResponseHolder.remove(payload.getId());
        if (count != null && count.getCount() == 1) {
            count.countDown();
        }
    }

    @Override
    public boolean onInterceptWsMessage(@NonNull WsMessage message) {
        return WsMessage.BIND_LOVE_INFO.equals(message.getInfo());
    }


    @Override
    public User queryUser(String username) {
        ValueOperations<String, User> ops = userRedisTemplate.opsForValue();
        String cacheKey = CACHE_USER_PRE_KEY + username;
        User user = ops.get(cacheKey);
        if (user == null) {
            LogUtils.d(TAG, "queryUser from mysql " + username);
            user = userMapper.selectByPrimaryKey(username);
        } else {
            LogUtils.d(TAG, "queryUser from redis" + username);
        }
        if (user != null) {
            ops.setIfAbsent(cacheKey, user, 1, TimeUnit.HOURS);
        }
        return user;
    }

    @Override
    public Integer registerUser(User user) {
        User temp = queryUser(user.getUsername());
        if (temp != null) {
            return RegisterUserResult.REGISTERED;
        }
        try {
            userMapper.insert(user);
            return RegisterUserResult.SUCCESS;
        } catch (Exception e) {
            return RegisterUserResult.FAILED;
        }
    }

    @Autowired
    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Autowired
    public void setUserPool(IUserPool userPool) {
        this.userPool = userPool;
    }


    @Autowired
    public void setDispatcher(WsDispatcher dispatcher) {
        dispatcher.addWsMessageInterceptor(this);
    }


    @Autowired
    @Qualifier("user-cache")
    public void setUserRedisTemplate(RedisTemplate<String, User> userRedisTemplate) {
        this.userRedisTemplate = userRedisTemplate;
    }
}
